package com.mvp.dagger.modules;

import android.app.Application;
import android.text.TextUtils;

import com.mvp.base.ApplicationInter;
import com.mvp.base.BaseApplication;
import com.mvp.common.GlobalValue;
import com.mvp.common.LocalManageUtil;
import com.mvp.dagger.converter.ResponseConvertFactory;
import com.mvp.networking.AuthenticationInterceptor;
import com.mvp.utils.AppUtil;
import com.mvp.utils.https.NetworkUtil;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.cert.CertificateException;
import java.util.concurrent.TimeUnit;

import javax.inject.Singleton;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import dagger.Module;
import dagger.Provides;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import timber.log.Timber;

import static java.lang.System.out;
import static timber.log.Timber.d;

/**
 * Created by zhangyuncai on 2018/9/11.
 */


@Module
public final class NetworkModule {

    public static final String LINE_SEPARATOR = System.getProperty("line.separator");
    /**
     * volatile 修饰的成员变量在每次被线程访问时，都强迫从共享内存中重读该成员变量的值。而且，当成员变量发生变化
     * 时，强迫线程将变化值回写到共享内存。这样在任何时刻，两个不同的线程总是看到某个成员变量的同一个值。
     */
    private static volatile Retrofit retrofit;

    private NetworkModule() {

        throw new AssertionError();
    }

    public static Retrofit getRetrofit() {
        if (retrofit == null) {
            HttpLoggingInterceptor httpLoggingInterceptor = providesHttpLoggingInterceptor();
            OkHttpClient okHttpClient = provideOkHttpClient(httpLoggingInterceptor, BaseApplication.sApplication);
            retrofit = providesRetrofit(okHttpClient, BaseApplication.sApplication);
        }
        return retrofit;
    }


    public static void printJson(String msg) {

        String message;

        try {
            if (msg.startsWith("{")) {
                JSONObject jsonObject = new JSONObject(msg);
                message = jsonObject.toString(4);//最重要的方法，就一行，返回格式化的json字符串，其中的数字4是缩进字符数
            } else if (msg.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(msg);
                message = jsonArray.toString(4);
            } else {
                message = msg;
            }
        } catch (JSONException e) {
            message = msg;
        }

        Timber.d("╔═══════════════════════════════════════════════════════════════════════════════════════");
        message = LINE_SEPARATOR + message;
        String[] lines = message.split(LINE_SEPARATOR);
        for (String line : lines) {
//            LogUtil.d("", line);
            Timber.d(line);
        }
        Timber.d("╚═══════════════════════════════════════════════════════════════════════════════════════");
    }

    @Provides
    @Singleton
    public static HttpLoggingInterceptor providesHttpLoggingInterceptor() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                if (!TextUtils.isEmpty(message)) {
                    String s = message.substring(0, 1);
                    //如果收到响应是json才打印
                    if ("{".equals(s) || "[".equals(s)) {
                        printJson(message);
                        return;
                    } else if (message.contains("&") && GlobalValue.isDebug) {
                        for (String spit : message.split("&")) {
                            try {
                                spit = URLDecoder.decode(spit, "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                            out.println("---<<<>>>---参数:" + spit);
//                            Timber.d("参数:" + spit);
                        }
                    }
                }
                d("" + message);
            }
        });
//        if (GlobalValue.isDebug) {
        if (true) {
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        } else {
            interceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
        }
        return interceptor;
    }


    @Provides
    @Singleton
    public static OkHttpClient provideOkHttpClient(HttpLoggingInterceptor httpLoggingInterceptor,
                                                   Application application) {
        File httpCacheDirectory = new File(application.getCacheDir(), "a5b_app_cache");
        Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);//10m
        final OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();

        try {
            okHttpBuilder.sslSocketFactory(addSSL());
        } catch (Exception e) {
            e.printStackTrace();
        }
        okHttpBuilder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });

        okHttpBuilder.connectTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS);
        okHttpBuilder.writeTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS);
        okHttpBuilder.readTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS);
//        okHttpBuilder.retryOnConnectionFailure(true);

        // Adds authentication headers when required in network calls
        okHttpBuilder.addNetworkInterceptor(new AuthenticationInterceptor());
        //拦截器 日志,所有的请求响应都看到
        okHttpBuilder.addInterceptor(httpLoggingInterceptor);
        okHttpBuilder.addInterceptor(addHeaderInterceptor(application));
        //拦截器 添加公共参数
//        okHttpBuilder.addInterceptor(addQueryParameterInterceptor());//自定义拦截器 请求体 的 公共参数 拦截
        //重写拦截器  Interceptor  设置缓存
//        okHttpBuilder.addNetworkInterceptor(addCacheInterceptor(application));//自定义拦截器 网络缓存 拦截
        //错误重连
        okHttpBuilder.retryOnConnectionFailure(true);
        //okhttp 添加缓存
        okHttpBuilder.cache(cache);
        /*if (GlobalValue.isCom()) {//正式环境
            try {
                //添加https 证书
                HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(new InputStream[]{new Buffer().writeUtf8(GlobalValue.SIGN_INFO).inputStream()}, null, null);
                okHttpBuilder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
            } catch (Exception e) {
                e.printStackTrace();
            }
            okHttpBuilder.hostnameVerifier((hostname, session) -> true);
        }*/
        return okHttpBuilder.build();
    }

    private static SSLSocketFactory addSSL() throws Exception {
        // Create a trust manager that does not validate certificate chains
        final TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                    }

                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                    }

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
        };

        // Install the all-trusting trust manager
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        // Create an ssl socket factory with our all-trusting manager
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        return sslSocketFactory;
    }

    private static final long TIMEOUT_CONNECT = 30;

    @Provides
    @Singleton
    public static Retrofit providesRetrofit(OkHttpClient okHttpClient, Application application) {
        //创建缓存文件 指定路径 缓存名称

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(GlobalValue.BASE_API_URL)
//                .validateEagerly(false)// Fail early: check Retrofit configuration at creation time in Debug build.
//                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                .addConverterFactory(ResponseConvertFactory.create(application))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .build();
        return retrofit;
    }


    //重写拦截器  Interceptor  给请求的头部  添加token
    private static Interceptor addHeaderInterceptor(Application application) {
        Interceptor headerInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request originalRequest = chain.request();//拿到原来的请求

                String method = originalRequest.method().toLowerCase().trim();

                RequestBody originalRequestBody = originalRequest.body();
                if (TextUtils.equals(method, "post") && (originalRequestBody instanceof FormBody)) {
                    FormBody originalFormBody = (FormBody) originalRequestBody;
                    FormBody.Builder bodyBuilder = new FormBody.Builder();
                    //把原来的参数添加到新的构造器，（因为没找到直接添加，所以就new新的）
                    for (int i = 0; i < originalFormBody.size(); i++) {
                        if (TextUtils.equals(originalFormBody.encodedName(i), "app_os") ||
                                TextUtils.equals(originalFormBody.encodedName(i), "app_register_id") ||
                                TextUtils.equals(originalFormBody.encodedName(i), "lang")) {
                            continue;
                        }
                        bodyBuilder.addEncoded(originalFormBody.encodedName(i), originalFormBody.encodedValue(i));
                    }
                    //统一添加到body中
                    originalRequestBody = bodyBuilder
                            .addEncoded("app_os", "1")//1 = Android , 2 = IOS
                            .addEncoded("app_register_id", ((ApplicationInter) application).getRegistrationId() + "")
                            .addEncoded("lang", LocalManageUtil.getAppLanguage() + "")
                            .build();

                }

                //请求的Builder拦截 这里只进行头部拦截  添加token
                Request.Builder requestBuilder = originalRequest.newBuilder()
                        // 头部添加 token
                        .header("app_os", "1")
                        .header("app_register_id", ((ApplicationInter) application).getRegistrationId() + "")
                        .header("language_code", LocalManageUtil.getAppLanguage() + "")
                        .removeHeader("User-Agent")
                        .addHeader("User-Agent", AppUtil.INSTANCE.getUserAgent(application))
                        //请求方法  如有需要也可拦截添加
                        .method(originalRequest.method(), originalRequestBody);

                Request request = requestBuilder.build();//拦截过后返回的请求

                //chain.proceed(request);拦截的核心方法，通过调用该方法获取新的请求响应
                return chain.proceed(request);
            }
        };
        return headerInterceptor;
    }

    //重写拦截器  Interceptor  设置缓存
    private static Interceptor addCacheInterceptor(Application application) {

        Interceptor cacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request originalRequest = chain.request();//拿到原来的请求

                //网络不可用
                if (!NetworkUtil.isNetworkAvailable(application)) {//检查网络不可用
                    //如果网络不可用，数据从缓存中获取
                    originalRequest = originalRequest.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)// okhttp 从缓存中获取
                            //.cacheControl(CacheControl.FORCE_NETWORK)//okhttp 从网络中获取
                            .build();
                }
                //chain.proceed(originalRequest);拦截的核心方法，通过调用该方法获取新的请求响应
                Response response = chain.proceed(originalRequest);//执行 拦截  返回请求体


                //网络可用
                if (NetworkUtil.isNetworkAvailable(application)) {
                    int maxAge = 0;
                    // 有网络时 设置缓存超时时间0个小时 ,意思就是不读取缓存数据,只对get有用,post没有缓冲
                    response.newBuilder()
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            // 清除头信息，因为服务器如果不支持，会返回一些干扰信息，不清除下面无法生效
                            .removeHeader("Retrofit")
                            .build();
                } else {
                    // 无网络时，设置超时为4周  只对get有用,post没有缓冲
                    int maxStale = 60 * 60 * 24 * 28;
                    response.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=" +
                                    maxStale)
                            .removeHeader("nyn")
                            .build();
                }
                return response;
            }
        };
        return cacheInterceptor;
    }

}